package top.util.concurrent.locks;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author He Kuntao
 * @since 2025/5/20 10:59
 */
public class FReentrantLock implements Lock {

    public FReentrantLock() {
        sync = new NonfairSync();
    }

    public FReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    @Override
    public void lock() {
        sync.lock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.nonFairTryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }


    /**
     * 抽象同步器
     */
    private final Sync sync;

    /**
     * RLock同步器抽象类
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {

        /**
         * 抽象lock，让子类实现公平与非公平
         */
        abstract void lock();

        /**
         * 非公平-尝试获取锁
         *
         * @param acquire 数量
         * @return true获取成功，反之false
         */
        final boolean nonFairTryAcquire(int acquire) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 非公平获取锁，不论排队队列，依然直接尝试获取
                if (compareAndSetState(0, acquire)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (current == getExclusiveOwnerThread()) {
                int nextState = c + acquire;
                if (nextState < 0) {
                    throw new Error("Maximum lock count exceeded");
                }
                setState(nextState);
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            }
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        /**
         * 创建Condition
         *
         * @return Condition
         */
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }


    /**
     * 公平锁
     */
    static final class FairSync extends Sync {

        @Override
        void lock() {
            acquire(1);
        }

        @Override
        protected boolean tryAcquire(int acquire) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 如果没有排队，并且CAS成功，则获取锁成功
                if (!hasQueuedPredecessors() && compareAndSetState(0, acquire)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }

            } else if (current == getExclusiveOwnerThread()) {
                int nextState = c + acquire;
                if (nextState < 0) {
                    throw new Error("Maximum lock count exceeded");
                }
                setState(nextState);
                return true;
            }
            // c != 0，非重入，尝试获取锁失败
            return false;
        }
    }


    /**
     * 非公平锁
     */
    static final class NonfairSync extends Sync {

        @Override
        void lock() {
            // 立即CAS尝试获取锁，失败后进入正常队列等待
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
            } else {
                acquire(1);
            }
        }

        @Override
        protected boolean tryAcquire(int acquire) {
            return nonFairTryAcquire(acquire);
        }
    }
}
